package net.mooctest;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.provider.CsvSource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class LinkedMultiTreeNodeTest {
    private LinkedMultiTreeNode<String> treeRoot;

    @BeforeEach
    void setUp() {
        treeRoot = new LinkedMultiTreeNode<>("root");
    }

    @Nested
    @DisplayName("add方法测试")
    class AddTest {

        @ParameterizedTest
        @CsvSource({"level_1"})
        @DisplayName("测试添加非null node")
        void add1(ArgumentsAccessor args) {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>(args.getString(0));
            boolean result = treeRoot.add(linkedMultiTreeNode);

            assertTrue(result);
        }

        @Test
        @DisplayName("测试添加  null node")
        void add2() {
            boolean result = treeRoot.add(null);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1"})
        @DisplayName("当前节点非叶结点添加非null节点")
        void add3(ArgumentsAccessor args) {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>(args.getString(0));
            treeRoot.add(linkedMultiTreeNode);
            boolean result = treeRoot.add(linkedMultiTreeNode);

            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("clear方法测试")
    class ClearTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试子树不为空")
        void clear1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            treeRoot.clear();

            assertTrue(treeRoot.isLeaf());
        }

        @Test
        @DisplayName("测试子树为空")
        void clear2() {
            treeRoot.clear();

            assertTrue(treeRoot.isLeaf());
        }
    }

    @Nested
    @DisplayName("contains方法测试")
    class ContainsTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点存在,且为第一个节点")
        void contains1(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.contains(linkedMultiTreeNodeList.get(0));

            assertTrue(result);
        }

        @Test
        @DisplayName("子树为空")
        void contains2() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
            boolean result = treeRoot.contains(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点为null")
        void contains3(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            boolean result = treeRoot.contains(null);

            assertFalse(result);
        }

        @Test
        @DisplayName("当前节点为叶节点")
        void contains4() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getLeafNode();
            boolean result = treeRoot.contains(linkedMultiTreeNode);

            assertFalse(result);
        }

        @Test
        @DisplayName("查询的节点是根节点")
        void contains5() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getRootNode();
            boolean result = treeRoot.contains(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点存在,且不为第一个节点")
        void contains6(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            List<LinkedMultiTreeNode<String>> subLinkedMultiTreeNodeList = addSubtree(linkedMultiTreeNodeList.get(0), args);
            boolean result = treeRoot.contains(subLinkedMultiTreeNodeList.get(1));

            assertTrue(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点不存在")
        void contains7(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            boolean result = treeRoot.contains(leafNode);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("dropSubtree方法测试")
    class DropSubtreeTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点存在,且为第一个节点")
        void dropSubtree1(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.dropSubtree(linkedMultiTreeNodeList.get(0));

            assertTrue(result);
        }

        @Test
        @DisplayName("子树为空")
        void dropSubtree2() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
            boolean result = treeRoot.dropSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点为null")
        void dropSubtree3(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            boolean result = treeRoot.dropSubtree(null);

            assertFalse(result);
        }

        @Test
        @DisplayName("当前节点为叶节点")
        void dropSubtree4() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getLeafNode();
            boolean result = treeRoot.dropSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @Test
        @DisplayName("删除的节点是根节点")
        void dropSubtree5() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getRootNode();
            boolean result = treeRoot.dropSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点存在,且为该节点子树的子树")
        void dropSubtree6(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            List<LinkedMultiTreeNode<String>> subLinkedMultiTreeNodeList = addSubtree(linkedMultiTreeNodeList.get(0), args);
            boolean result = treeRoot.dropSubtree(subLinkedMultiTreeNodeList.get(1));

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点不存在")
        void dropSubtree7(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            boolean result = treeRoot.dropSubtree(leafNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点不是存在且不是第一个节点")
        void dropSubtree8(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.dropSubtree(linkedMultiTreeNodeList.get(1));

            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("hasSubtree方法测试")
    class HasSubtreeTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点存在,且为第一个节点")
        void hasSubtree1(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.hasSubtree(linkedMultiTreeNodeList.get(0));

            assertTrue(result);
        }

        @Test
        @DisplayName("子树为空")
        void hasSubtree2() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
            boolean result = treeRoot.hasSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点为null")
        void hasSubtree3(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            boolean result = treeRoot.hasSubtree(null);

            assertFalse(result);
        }

        @Test
        @DisplayName("当前节点为叶节点")
        void hasSubtree4() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getLeafNode();
            boolean result = treeRoot.hasSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @Test
        @DisplayName("查询的节点是根节点")
        void hasSubtree5() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getRootNode();
            boolean result = treeRoot.hasSubtree(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点不是存在且不是第一个节点")
        void hasSubtree6(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.hasSubtree(linkedMultiTreeNodeList.get(1));

            assertTrue(result);
        }


        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查询的节点不存在")
        void hasSubtree7(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            boolean result = treeRoot.hasSubtree(leafNode);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("height方法测试")
    class HeightTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("当前节点不为叶节点")
        void height1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            int height = treeRoot.height();

            assertEquals(1, height);
        }


        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("当前节点为叶节点")
        void height2(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            int result = linkedMultiTreeNodeList.get(0).height();

            assertEquals(0, result);
        }
    }

    @Nested
    @DisplayName("isLeaf方法测试")
    class IsLeafTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("当前节点不为叶节点")
        void isLeaf1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            boolean result = treeRoot.isLeaf();

            assertFalse(result);
        }


        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("当前节点为叶节点")
        void isLeaf2(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = linkedMultiTreeNodeList.get(0).isLeaf();

            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("remove方法测试")
    class RemoveTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点存在")
        void remove1(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            boolean result = treeRoot.remove(linkedMultiTreeNodeList.get(0));

            assertTrue(result);
        }

        @Test
        @DisplayName("子树为空")
        void remove2() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
            boolean result = treeRoot.remove(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("删除的节点为null")
        void remove3(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            boolean result = treeRoot.remove(null);

            assertFalse(result);
        }

        @Test
        @DisplayName("当前节点为叶节点")
        void remove4() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getLeafNode();
            boolean result = treeRoot.remove(linkedMultiTreeNode);

            assertFalse(result);
        }

        @Test
        @DisplayName("删除的节点是根节点")
        void remove5() {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = getRootNode();
            boolean result = treeRoot.remove(linkedMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("刪除的节点不存在")
        void remove6(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            boolean result = treeRoot.remove(leafNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("刪除的节点为当前节点的子节点的子节点")
        void remove7(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            List<LinkedMultiTreeNode<String>> subLinkedMultiTreeNodeList = addSubtree(linkedMultiTreeNodeList.get(1), args);
            boolean result = treeRoot.remove(subLinkedMultiTreeNodeList.get(0));

            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("siblings方法测试")
    class SiblingsTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找非根节点")
        void siblings1(ArgumentsAccessor args) {
            List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = addSubtree(treeRoot, args);
            Collection<LinkedMultiTreeNode<String>> result = (Collection<LinkedMultiTreeNode<String>>) linkedMultiTreeNodeList.get(0).siblings();
            System.out.println(result);
        }

        @Test
        @DisplayName("当前节点为根节点,对于异常进行审核")
        void siblings2() {
            Throwable TNExcp = assertThrows(TreeNodeException.class, () -> {
                treeRoot.siblings();
            });
            System.out.println(TNExcp.getMessage());

            assertEquals(String.format("Unable to find the siblings. The tree node %1$s is root", treeRoot), TNExcp.getMessage());
        }

        @Test
        @DisplayName("当前节点无兄弟节点")
        void siblings3() {
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            Collection<LinkedMultiTreeNode<String>> result = (Collection<LinkedMultiTreeNode<String>>) leafNode.siblings();
            System.out.println(result);
        }
    }

    @Nested
    @DisplayName("subtrees方法测试")
    class SubtreesTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找根节点的子树")
        void subtrees1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            Collection<LinkedMultiTreeNode<String>> result = (Collection<LinkedMultiTreeNode<String>>) treeRoot.subtrees();
            System.out.println(result);
        }

        @Test
        @DisplayName("当前节点为叶节点")
        void subtrees2() {
            LinkedMultiTreeNode<String> leafNode = getLeafNode();
            Collection<LinkedMultiTreeNode<String>> result = (Collection<LinkedMultiTreeNode<String>>) leafNode.subtrees();
            System.out.println(result);
        }
    }

    @Nested
    @DisplayName("iterator方法测试")
    class IteratorTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试iterator")
        void iterator1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);

            LinkedMultiTreeNode<String>.TreeNodeIterator iterator = treeRoot.iterator();
            System.out.println(iterator);

            TreeNode<String> leftMostNode = iterator.leftMostNode();
            TreeNode<String> rightSiblingNode = iterator.rightSiblingNode();
            System.out.println(leftMostNode);
            System.out.println(rightSiblingNode);
        }

    }

    @Nested
    @DisplayName("traversePreOrder方法测试")
    class TraversePreOrderTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试traversePreOrder")
        void traversePreOrder1(ArgumentsAccessor args) {
            Collection<TreeNode<String>> mPreOrdered = new ArrayList<>();
            TraversalAction<TreeNode<String>> action = populateAction(mPreOrdered);
            treeRoot.traversePreOrder(action);
        }

    }

    @Nested
    @DisplayName("traversePostOrder方法测试")
    class TraversePostOrderTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试traversePostOrder")
        void traversePostOrder1(ArgumentsAccessor args) {
            addSubtree(treeRoot, args);
            Collection<TreeNode<String>> mPreOrdered = new ArrayList<>();
            TraversalAction<TreeNode<String>> action = populateAction(mPreOrdered);
            treeRoot.traversePostOrder(action);
        }

    }

    /**
     * Populates the input collection with the tree nodes, while traversing the tree
     * <p>
     * copy from TreeNode
     *
     * @param collection input collection to populate
     * @param <T>        type of the tree node
     * @return traversal action, which populates the input collection with the tree nodes
     */
    protected static <T> TraversalAction<TreeNode<T>> populateAction(final Collection<TreeNode<T>> collection) {
        return new TraversalAction<TreeNode<T>>() {
            @Override
            public void perform(TreeNode<T> node) {
                collection.add(node);
            }

            @Override
            public boolean isCompleted() {
                return false;
            }
        };
    }


    /**
     * 获得一个根节点
     *
     * @return 根节点
     */
    private LinkedMultiTreeNode<String> getRootNode() {
        LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
        return linkedMultiTreeNode;
    }

    /**
     * 获得一个叶节点
     *
     * @return 叶节点
     */
    private LinkedMultiTreeNode<String> getLeafNode() {
        LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>("test");
        LinkedMultiTreeNode<String> leaf = new LinkedMultiTreeNode<>("leaf");
        linkedMultiTreeNode.add(leaf);
        return leaf;
    }


    /**
     * 将args中的数据添加到treeRoot的子树中
     *
     * @param args 传入的csv文件参数
     * @return 生成的数组对象
     */
    private List<LinkedMultiTreeNode<String>> addSubtree(LinkedMultiTreeNode<String> root, ArgumentsAccessor args) {
        List<LinkedMultiTreeNode<String>> linkedMultiTreeNodeList = new ArrayList<>();
        for (int i = 0; i < args.size(); i++) {
            LinkedMultiTreeNode<String> linkedMultiTreeNode = new LinkedMultiTreeNode<>(args.getString(i));
            linkedMultiTreeNodeList.add(linkedMultiTreeNode);
            root.add(linkedMultiTreeNode);
        }

        return linkedMultiTreeNodeList;
    }
}